﻿#include "open62541.h"

Open62541::Open62541()
{
    client = UA_Client_new();
    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
}

bool Open62541::Open62541_Init(QString endpointUrl)
{
    //连接Kepware OPC UA服务器（替换为你的Kepware IP和端口）
    UA_StatusCode statusCode = UA_Client_connect(client, endpointUrl.toStdString().c_str());
    if(statusCode != UA_STATUSCODE_GOOD)
    {
        qDebug() << u8"连接kepware失败，错误码 = " << QString::number(statusCode, 16).prepend("0x");
        return false;
    }

    return true;
}

bool Open62541::Open62541_WriteData(int nsIndex, QString tagPath, bool data)
{
    //写入单个bool类型
    UA_Variant value;
    UA_Variant_init(&value);

    UA_Boolean booleanValue = data;

    /*
    int aaa[10];
    aaa[QVariant::Invalid] = UA_TYPES_BYTE;
    aaa[QVariant::Bool] = UA_TYPES_BOOLEAN;
    UA_Variant_setScalar(&value, &booleanValue, &UA_TYPES[aaa[data.type()]]);
    */
    UA_Variant_setScalar(&value, &booleanValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
    UA_StatusCode statusCode = UA_Client_writeValueAttribute(client, UA_NODEID_STRING_ALLOC(nsIndex, tagPath.toStdString().c_str()), &value);
    if(statusCode == UA_STATUSCODE_GOOD)
    {
        qDebug() << QString(u8"bool值 %1 写入完成").arg(tagPath.toStdString().c_str());
        return true;
    }
    else
    {
        qDebug() << QString("Failed to UA_Client_readValueAttribute ns=%1,i=%2, statusCode: 0x%3")
               .arg(nsIndex)
               .arg(tagPath.toStdString().c_str())
               .arg(statusCode, 8, 16, QChar('0'));

        return false;
    }

}

bool Open62541::Open62541_WriteData(int nsIndex, QString tagPath, int16_t data)
{
    //写入单个short类型（Int16)
    UA_Variant value;
    UA_Variant_init(&value);

    UA_Int16 int16Value = data;
    UA_Variant_setScalar(&value, &int16Value, &UA_TYPES[UA_TYPES_INT16]);

    UA_StatusCode statusCode = UA_Client_writeValueAttribute(client, UA_NODEID_STRING_ALLOC(nsIndex, tagPath.toStdString().c_str()), &value);
    if(statusCode == UA_STATUSCODE_GOOD)
    {
        qDebug() << QString(u8"int16_t值 %1 写入完成").arg(tagPath.toStdString().c_str());
        return true;
    }
    else
    {
        qDebug() << QString("Failed to UA_Client_readValueAttribute ns=%1,i=%2, statusCode: 0x%3")
               .arg(nsIndex)
               .arg(tagPath.toStdString().c_str())
               .arg(statusCode, 8, 16, QChar('0'));

        return false;
    }
}

bool Open62541::Open62541_WriteData(int nsIndex, QString tagPath, uint8_t data)
{
    //写入单个uint8_t类型
    UA_Variant value;
    UA_Variant_init(&value);

    UA_Byte byteValue = data;
    UA_Variant_setScalar(&value, &byteValue, &UA_TYPES[UA_TYPES_INT16]);

    UA_StatusCode statusCode = UA_Client_writeValueAttribute(client, UA_NODEID_STRING_ALLOC(nsIndex, tagPath.toStdString().c_str()), &value);
    if(statusCode == UA_STATUSCODE_GOOD)
    {
        qDebug() << QString(u8"uint8_t 值 %1 写入完成").arg(tagPath.toStdString().c_str());
        return true;
    }
    else
    {
        qDebug() << QString("Failed to UA_Client_readValueAttribute ns=%1,i=%2, statusCode: 0x%3")
               .arg(nsIndex)
               .arg(tagPath.toStdString().c_str())
               .arg(statusCode, 8, 16, QChar('0'));

        return false;
    }
}

bool Open62541::Open62541_ReadData(int nsIndex, QString tagPath, bool &data)
{
    // 读取bool类型
    UA_Variant value;
    UA_Variant_init(&value);

    UA_StatusCode statusCode = UA_Client_readValueAttribute(client, UA_NODEID_STRING_ALLOC(nsIndex, tagPath.toStdString().c_str()), &value);
    if(   statusCode == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_BOOLEAN]))
    {
        data = *(UA_Boolean*)value.data;
        //qDebug() << "read bool data = " << *(UA_Boolean*)value.data;
        return true;
    }
    else
    {
        qDebug() << QString("Failed to UA_Client_readValueAttribute ns=%1,i=%2, statusCode: 0x%3")
               .arg(nsIndex)
               .arg(tagPath.toStdString().c_str())
               .arg(statusCode, 8, 16, QChar('0'));
        return false;
    }
}

bool Open62541::Open62541_ReadData(int nsIndex, QString tagPath, int16_t &data)
{
    //读取short类型（Int16)
    UA_Variant value;
    UA_Variant_init(&value);

    UA_StatusCode statusCode = UA_Client_readValueAttribute(client, UA_NODEID_STRING_ALLOC(nsIndex, tagPath.toStdString().c_str()), &value);
    if(   statusCode == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_INT16]))
    {
        data = *(UA_Int16*)value.data;
        //qDebug() << "read Int16(short) data = " << *(UA_Int16*)value.data;
        return true;
    }
    else
    {
        qDebug() << QString("Failed to UA_Client_readValueAttribute ns=%1,i=%2, statusCode: 0x%3")
               .arg(nsIndex)
               .arg(tagPath.toStdString().c_str())
               .arg(statusCode, 8, 16, QChar('0'));
        return false;
    }
}

bool Open62541::Open62541_ReadData(int nsIndex, QString tagPath, uint8_t &data)
{
    //读取byte类型（uint8_t)
    UA_Variant value;
    UA_Variant_init(&value);

    UA_StatusCode statusCode = UA_Client_readValueAttribute(client, UA_NODEID_STRING_ALLOC(nsIndex, tagPath.toStdString().c_str()), &value);
    if(   statusCode == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_BYTE]))
    {
        data = *(UA_Byte*)value.data;
        //qDebug() << "read uint8_t(byte) data = " << *(UA_Byte*)value.data;
        return true;
    }
    else
    {
        qDebug() << QString("Failed to UA_Client_readValueAttribute ns=%1,i=%2, statusCode: 0x%3")
               .arg(nsIndex)
               .arg(tagPath.toStdString().c_str())
               .arg(statusCode, 8, 16, QChar('0'));
        return false;
    }
}

bool Open62541::Open62541_ReadData(int nsIndex, QString tagPath, int32_t &data)
{
    //读取Dint类型
    UA_Variant value;
    UA_Variant_init(&value);

    UA_StatusCode statusCode = UA_Client_readValueAttribute(client, UA_NODEID_STRING_ALLOC(nsIndex, tagPath.toStdString().c_str()), &value);
    if(   statusCode == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_INT32]))
    {
        data = *(UA_Int32*)value.data;
        return true;
    }
    else
    {
        qDebug() << QString("Failed to UA_Client_readValueAttribute ns=%1,i=%2, statusCode: 0x%3")
               .arg(nsIndex)
               .arg(tagPath.toStdString().c_str())
               .arg(statusCode, 8, 16, QChar('0'));
        return false;
    }
}

bool Open62541::Open62541_batchReading(int nsIndex, QStringList tagPaths, QMap<QString, QVariant>& data)
{
    //创建返回数据
    QMap<QString, QVariant> result;

    //准备批量读取的请求参数
    int tagCount = tagPaths.size();

    UA_ReadValueId *nodesToRead = UA_ReadValueId_new();
    UA_ReadValueId_init(nodesToRead);
    nodesToRead = (UA_ReadValueId*)UA_realloc(nodesToRead, tagCount * sizeof(UA_ReadValueId));

    // 为每个标签设置NodeId，Kepware的标签路径
    for (int i = 0; i < tagCount; ++i)
    {
        QString tagPath = tagPaths[i];
        //qDebug() << tagPath;
        UA_ReadValueId_init(&nodesToRead[i]);

        // 创建标签的NodeId
        nodesToRead[i].nodeId = UA_NODEID_STRING_ALLOC(nsIndex, tagPath.toUtf8().constData());
        //qDebug() << tagPath.toUtf8().constData();
        nodesToRead[i].attributeId = UA_ATTRIBUTEID_VALUE; // 读取值属性
    }

    // 配置读取请求
    UA_ReadRequest readRequest;
    UA_ReadRequest_init(&readRequest);
    readRequest.nodesToRead = nodesToRead;
    readRequest.nodesToReadSize = tagCount;

    // 执行批量读取
    UA_ReadResponse readResponse = UA_Client_Service_read(client, readRequest);

    // 处理读取结果
    bool success = true;
    if (readResponse.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
    {
        for (int i = 0; i < readResponse.resultsSize; ++i)
        {
            if (readResponse.results[i].status == UA_STATUSCODE_GOOD)
            {
                // 转换并存储结果
                data[tagPaths[i]] = variantFromUaValue(readResponse.results[i].value);
            }
            else
            {
                success = false;
                data[tagPaths[i]] = QVariant();
            }
        }

        return success;
    }
    else
    {
        qDebug() << "read error:" << readResponse.responseHeader.serviceResult;
        return false;
    }
}


QVariant Open62541::variantFromUaValue(const UA_Variant &uaVariant)
{
    // 根据OPC UA数据类型转换为QVariant
    if (UA_Variant_hasScalarType(&uaVariant, &UA_TYPES[UA_TYPES_BOOLEAN])) {
        return QVariant(*(UA_Boolean*)uaVariant.data != 0);
    }
    else if (UA_Variant_hasScalarType(&uaVariant, &UA_TYPES[UA_TYPES_INT16])) {
        return QVariant(*(UA_Int16*)uaVariant.data);
    }
    else if (UA_Variant_hasScalarType(&uaVariant, &UA_TYPES[UA_TYPES_BYTE])) {
        return QVariant(*(UA_Byte*)uaVariant.data);
    }
    else if (UA_Variant_hasScalarType(&uaVariant, &UA_TYPES[UA_TYPES_DOUBLE])) {
        return QVariant(*(UA_Double*)uaVariant.data);
    }
    else if (UA_Variant_hasScalarType(&uaVariant, &UA_TYPES[UA_TYPES_STRING])) {
        UA_String *str = (UA_String*)uaVariant.data;
        return QVariant(QString::fromUtf8((const char*)str->data, str->length));
    }
    else {
        qDebug() << "=ID:" << uaVariant.type;
        return QVariant();
    }
}

bool Open62541::convertToUaVariant(const QVariant &qVar, UA_Variant &uaVar)
{
    UA_Variant_init(&uaVar);
    bool success = true;

    // 获取QVariant的类型信息
    QVariant::Type type = qVar.type();
    int userType = qVar.userType();

    // 1. 处理bool类型
    if (type == QVariant::Bool) {
        UA_Boolean value = static_cast<UA_Boolean>(qVar.toBool());
        UA_Variant_setScalar(&uaVar, &value, &UA_TYPES[UA_TYPES_BOOLEAN]);
    }
    // 2. 处理byte类型（quint8）
    // 兼容方案：判断是否为无符号整数且值在0-255范围内
    else if (type == QVariant::UInt) {
        quint32 val = qVar.toUInt();
        if (val <= 0xFF) { // 0-255范围，视为quint8
            UA_Byte value = static_cast<UA_Byte>(val);
            UA_Variant_setScalar(&uaVar, &value, &UA_TYPES[UA_TYPES_BYTE]);
        } else {
            qWarning() << "值超出byte范围（0-255）:" << val;
            success = false;
        }
    }
    // 3. 处理short类型（qint16）
    else if (type == QVariant::Int) {
        qint32 val = qVar.toInt();
        if (val >= -32768 && val <= 32767) { // 16位有符号整数范围
            UA_Int16 value = static_cast<UA_Int16>(val);
            UA_Variant_setScalar(&uaVar, &value, &UA_TYPES[UA_TYPES_INT16]);
        } else {
            qWarning() << "值超出short范围（-32768~32767）:" << val;
            success = false;
        }
    }
    // 其他不支持的类型
    else {
        qWarning() << "不支持的数据类型，类型ID:" << userType
                   << "，类型名:" << QVariant::typeToName(type);
        success = false;
    }

    return success;
}


